﻿using KongMing.Project.Entity.Common;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.Security.Cryptography;
using System.Text;

namespace KongMing.Project.Common.ServiceExtension
{
    /// <summary>
    /// JWT*鉴权*服务注入
    /// </summary>
    public static class JwtExtension
    {
        public static void AddJwtAuthentication(this IServiceCollection services, JwtConfig jwtConfig)
        {

            #region 获取IssuerSigningKey，秘钥经过加密后得到的秘文
            //秘钥经过对称可逆加密后的秘文
            SymmetricSecurityKey symmetricSecurityKey = default;
            //秘钥经过非对称可逆加密后的秘文
            RsaSecurityKey rsaSecurityKey = default;
            if (jwtConfig.KeySymmetryType == 0)//对称可逆加密 
            {
                symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey));
            }
            else
            {
                //非对称可逆加密
                // 读取公钥  验证需要解密，解密就需要解密Key
                string path = Path.Combine(Directory.GetCurrentDirectory(), "key.public.json");
                string key = File.ReadAllText(path);//this.Configuration["SecurityKey"];
                var keyParams = JsonConvert.DeserializeObject<RSAParameters>(key);
                rsaSecurityKey = new RsaSecurityKey(keyParams);
            }
            #endregion

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer("Bearer",options =>
            {
                #region jwt验证参数设置
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    //JWT有一些默认的属性，就是给鉴权时就可以筛选了
                    ValidateIssuer = true,//是否验证Issuer--如果要验证---必须让token中的issuer和这里配置的一样
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = true,//是否验证失效时间
                    ValidateIssuerSigningKey = true,//是否验证SecurityKey
                    ValidAudience = jwtConfig.Audience,
                    ValidIssuer = jwtConfig.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                    IssuerSigningKey = jwtConfig.KeySymmetryType == 0 ? symmetricSecurityKey : rsaSecurityKey,
                    //JWT默认过期时间，不配置的话默认是5分钟，如果配置的过期时间claims小于默认过期时间，就是默认过期时间，否则就是配置的过期时间
                    ClockSkew = TimeSpan.FromSeconds(1),
                    //自定义验证Audience
                    //AudienceValidator = (m, n, z) =>
                    //{
                    //    //return m != null && m.FirstOrDefault().Equals(builder.Configuration["audience"]);
                    //    return true;
                    //},
                    //自定义验证失效时间
                    //LifetimeValidator = (notBefore, expires, securityToken, validationParameters) =>
                    //{
                    //    bool bResult = notBefore <= DateTime.Now
                    //    && expires >= DateTime.Now;
                    //    return bResult;
                    //}
                };
                #endregion

                //如果验证不通过，可以给一个事件注册一个动作，这动作就是指定返回的结果；

                #region jwt验证不通过，触发的事件，自定义响应体
                options.Events = new JwtBearerEvents
                {
                    //此处为权限验证失败后触发的事件
                    OnChallenge = context =>
                    {
                        //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                        context.HandleResponse();
                        //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换 
                        //自定义返回的数据类型
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码，默认为401 我这里改成 200
                        //context.Response.StatusCode = StatusCodes.Status200OK;
                        context.Response.WriteAsync(JsonConvert.SerializeObject(new BaseApiResult
                        {
                            code = StatusCodes.Status401Unauthorized,
                            msg = "身份验证失效！"
                        }));
                        return Task.FromResult(0);
                    }
                };
                #endregion

                options.SaveToken = true;//每次请求时都保存token到HttpContext中
            });
        }
    }
}
